/*******************************************************************************
** 文件名称：gm_utils.h
** 文件作用：通用工具宏和函数
** 编写作者：Tom Free 付瑞彪
** 编写时间：2020-08-06
** 文件备注：
**
** 更新记录：
**           2021-05-06 -> 创建文件
**                                                             <Tom Free 付瑞彪>
**
**           Copyright (c) 2018-2021 付瑞彪 All Rights Reserved
**
**       1 Tab == 4 Spaces     UTF-8     ANSI C Language(C99)
*******************************************************************************/
#ifndef __GM_UTILS_H__
#define __GM_UTILS_H__

#include "gm_utils_cfg.h"
#include "stdint.h"
#include "stddef.h"
#include "stdio.h"
#ifndef __cplusplus
#include "stdbool.h"
#endif  /* __cplusplus */

/* 通用bool类型 */
#ifndef bool_t
typedef bool bool_t;
#endif  /* bool_t */
#ifndef boolean
typedef bool boolean;
#endif  /* boolean */

/*
 * 温馨提示：
 * 此代码并非原创，摘抄自各种网络平台的优秀代码，本人将其整理和规范命名，
 * 以下宏定义可以方便我们代码的阅读和提高计算效率，
 * 但宏参数请不要带入复杂表达式，
 * 当然你要是大神当我没说
 */

/* 得到指定地址上的一个字节 */
#define GM_MEM_VAL_BYTE(addr)   (*((uint8_t*)(addr)))
/* 得到指定地址上的一个字，注意部分平台需要addr是字对齐的，否则会触发总线错误异常 */
#define GM_MEM_VAL_WORD(addr)   (*((uint16_t*)(addr)))
/* 得到指定地址上的一个双字，注意部分平台需要addr是双字对齐的，否则会触发总线错误异常 */
#define GM_MEM_VAL_DWORD(addr)  (*((uint32_t*)(addr)))


/* 求两个数的最大值*/
#define GM_VAL_MAX(x, y)        (((x) > (y)) ? (x) : (y))
/* 求两个数的最小值 */
#define GM_VAL_MIN(x, y)        (((x) < (y)) ? (x) : (y))


/* 得到一个field在结构体(type)中的偏移量 */
#define GM_FIELD_OFFSET_OF(type, field) ((size_t)&(((type*)0)->field))

/* 得到一个field在结构体(type)中所占用的字节数 */
#define GM_FIELD_SIZE_OF(type, field)   sizeof(((type*)0)->field)


/* 得到一个字节变量的地址 */
#define GM_MEM_ADDR_BYTE(var)   ((uint8_t*)&(var))
/* 得到一个字变量的地址 */
#define GM_MEM_ADDR_WORD(var)   ((uint16_t*)&(var))
/* 得到一个双字变量的地址 */
#define GM_MEM_ADDR_DWORD(var)  ((uint32_t*)&(var))

/* 获取一个字数据的低和高字节 */
#define GM_WORD_L8(x)           ((uint8_t)((uint16_t)(x) & 0xFF))
#define GM_WORD_H8(x)           ((uint8_t)((uint16_t)(x) >> 8))

/* 获取一个双字数据的低和高字 */
#define GM_DWORD_L16(x)         ((uint16_t)((uint32_t)(x) & 0xFFFF))
#define GM_DWORD_H16(x)         ((uint16_t)((uint32_t)(x) >> 16))

/* 获取一个双字数据的低和高字节 */
#define GM_DWORD_L16_L8(x)      ((uint8_t)((uint32_t)(x) & 0xFF))   /* 最低 */
#define GM_DWORD_L16_H8(x)      ((uint8_t)((uint32_t)(x) >> 8))     /* 次低 */
#define GM_DWORD_H16_L8(x)      ((uint8_t)((uint32_t)(x) >> 16))    /* 次高 */
#define GM_DWORD_H16_H8(x)      ((uint8_t)((uint32_t)(x) >> 24))    /* 最高 */
/* 获取一个双字数据的低和高字节 */
#define GM_DWORD_L8(x)          GM_DWORD_L16_L8(x)  /* 最低 */
#define GM_DWORD_ML8(x)         GM_DWORD_L16_H8(x)  /* 次低 */
#define GM_DWORD_MH8(x)         GM_DWORD_H16_L8(x)  /* 次高 */
#define GM_DWORD_H8(x)          GM_DWORD_H16_H8(x)  /* 最高 */


/* 2的N次方 */
#define GM_2_POW_N(n)           (1 << (n))
/* 2的N次方对应的尾数掩码 */
#define GM_2_MASK_N(n)          (GM_2_POW_N(n) - 1)

/* 返回一个比X大的最接近的2的N次方的倍数 */
#define GM_RND_2_POW_N(x, n)    ((((x) + GM_2_MASK_N(n)) >> n) << n)

/* 返回一个比X小的最接近的2的N次方的倍数 */
#define GM_MASK_2_POW_N(x, n)   (((x) >> n) << n)


/* 无溢出自增 */
#define GM_INC_NOVF(val)    (val = (((val) + 1) > (val)) ? (val) + 1 : (val))
/* 无溢出自减 */
#define GM_DEC_NOVF(val)    (val = (((val) - 1) < (val)) ? (val) - 1 : (val))


/* 返回数组元素的个数 */
#define GM_ARR_SIZE(arr)            (sizeof((arr)) / sizeof((arr[0])))
/* 返回字符串数组元素的个数 */
#define GM_STR_ARR_SIZE(str_arr)    (GM_ARR_SIZE(str_arr) - 1)


/******************************************************************************/
/** IO空间操作相关宏 **/
/******************************************************************************/
/* 对于IO空间映射在存储空间的结构，输入输出处理 */
#define GM_IO_BYTE(addr)            (*((volatile uint8_t*)(addr)))
#define GM_IO_WORD(addr)            (*((volatile uint16_t*)(addr)))
#define GM_IO_DWORD(addr)           (*((volatile uint32_t*)(addr)))
#define GM_IO_OUT_BYTE(addr, val)   (*((volatile uint8_t*)(addr)) = ((uint8_t)(val)))
#define GM_IO_OUT_WORD(addr, val)   (*((volatile uint16_t*)(addr)) = ((uint16_t)(val)))
#define GM_IO_OUT_DWORD(addr, val)  (*((volatile uint32_t*)(addr)) = ((uint32_t)(val)))
/******************************************************************************/



/******************************************************************************/
/** 码制转换操作相关宏 **/
/******************************************************************************/
/* BCD码转数值, 20H -> 20 */
#define GM_BCD_TO_HEX(x)    (((x) >> 4) * 10 + ((x) & 0x0F))
/* 数值转BCD码, 20 -> 20H */
#define GM_HEX_TO_BCD(x)    (((x) % 10) + ((((x) / 10) % 10) << 4))
/******************************************************************************/



/******************************************************************************/
/** ASCII字符操作相关宏 **/
/******************************************************************************/
/* 字符是否在某个区间范围内 */
#define GM_CHAR_IN_RANGE(c, s, e)   \
                            (((uint8_t)(c) >= (s)) && ((uint8_t)(c) <= (e)))
/* 字符是否是可打印字符 */
#define GM_CHAR_IS_PRINT(c) GM_CHAR_IN_RANGE(c, 0x20, 0x7F)
/* 判断ASCII字符是不是10进制的数字字符 */
#define GM_CHAR_IS_DEC(c)   GM_CHAR_IN_RANGE(c, '0', '9')
/* 判断ASCII字符是不是16进制的数字字符 */
#define GM_CHAR_IS_HEX(c)   (GM_CHAR_IN_RANGE(c, '0', '9') ||   \
                             GM_CHAR_IN_RANGE(c, 'a', 'f') ||   \
                             GM_CHAR_IN_RANGE(c, 'A', 'F') )
/* 是否是小写 */
#define GM_CHAR_IS_LOWER(c) GM_CHAR_IN_RANGE(c, 'a', 'z')
/* 是否是大写 */
#define GM_CHAR_IS_UPPER(c) GM_CHAR_IN_RANGE(c, 'A', 'Z')

/* 小写转大写 */
#define GM_UPCASE(c)        (GM_CHAR_IS_LOWER(c) ? ((c) - 0x20) : (c))
/* 大写转小写 */
#define GM_DOWNCASE(c)      (GM_CHAR_IS_UPPER(c) ? ((c) + 0x20) : (c))
/******************************************************************************/



/******************************************************************************/
/** 位操作相关宏 **/
/******************************************************************************/
/* 读取某位 */
#define GM_BIT_GET(x, bitn)             (((x) >> (bitn)) & 0x01u)
/* 设置某位的值为0 */
#define GM_BIT_SET0(x, bitn)            (x) &= (~(1 << (bitn)))
/* 设置某位的值为1 */
#define GM_BIT_SET1(x, bitn)            (x) |= (1 << (bitn))
/* 给某位置反 */
#define GM_BIT_INVERSE(x, bitn)         ((x) = (x) ^ (1 << (bitn)))
/* 字节串中某BIT值 */
#define GM_BIT_GET_OF_BYTES(arr, bitn)  (GM_BIT_GET(arr[(bitn) / 8], (bitn) % 8))
/* 字节串中设置某BIT为0 */
#define GM_BIT_SET0_OF_BYTES(arr, bitn) GM_BIT_SET0(arr[(bitn) / 8], (bitn) % 8))
/* 字节串中设置某BIT为1 */
#define GM_BIT_SET1_OF_BYTES(arr, bitn) GM_BIT_SET1(arr[(bitn) / 8], (bitn) % 8))
/******************************************************************************/



/******************************************************************************/
/** 对齐操作相关宏 **/
/******************************************************************************/
/* 对齐掩码，注意需要输入2的N次方的数据，其它数据输入会出错 */
#define GM_ALIGN_MASK(align)    (align - 1)
/* 向上对齐 */
#define GM_ALIGN_UP(x, align)   (((uint8_t*)(x) + GM_ALIGN_MASK(align)) & (~GM_ALIGN_MASK(align)))
/* 向下对齐 */
#define GM_ALIGN_DOWN(x, align) ((uint8_t*)(x) & ~GM_ALIGN_MASK(align))
/* 是否对齐 */
#define GM_IS_ALIGNED(x, align) (((uint8_t*)(x) & GM_ALIGN_MASK(align)) == 0)
/******************************************************************************/

/* 日期类型定义 */
typedef struct
{
    uint16_t year;
    uint8_t  month;
    uint8_t  day;
} gm_date_t;

/* 时间类型定义 */
typedef struct
{
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
} gm_time_t;

/*******************************************************************************
** 函数名称：gm_utils_get_num_cnt
** 函数作用：计算数据的10进制位数
** 输入参数：num - 32位数据
** 输出参数：10进制的位数
** 使用范例：size_t cnt = gm_utils_get_num_cnt(2033);
** 函数备注：
*******************************************************************************/
size_t gm_utils_get_num_cnt(uint32_t num);

/*******************************************************************************
** 函数名称：gm_utils_array_cmp
** 函数作用：数组比对
** 输入参数：arr1, arr2 - 数组，length - 长度
** 输出参数：是否一样
** 使用范例：bool res = gm_utils_array_cmp(arr1, arr2, 128);
** 函数备注：需要保证两个数组长度不小于长度值，否则系统会挂
*******************************************************************************/
bool gm_utils_array_cmp(uint8_t *arr1, uint8_t *arr2, size_t length);

/*******************************************************************************
** 函数名称：gm_utils_array_add
** 函数作用：数组加法
** 输入参数：arr1,arr2 - 数组，length - 长度
** 输出参数：是否存在进位，加法运算结果存放到了arr1中
** 使用范例：uint8_t carry = gm_utils_array_add(arr1, arr2, 10);
** 函数备注：需要保证两个数组长度不小于长度值，否则系统会挂
*******************************************************************************/
uint8_t gm_utils_array_add(uint8_t *arr1, uint8_t *arr2, size_t length);

#endif  /* __GM_UTILS_H__ */
